package com.simple.handler;

import com.simple.anno.AgentClass;
import com.simple.anno.AgentMethod;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public abstract class AbstractHandler implements Handler {

    /**
     * 自定义的代理类
     */
    protected Object proxy;

    /**
     * CGLIB代理增强类对象
     */
    protected Enhancer enhancer;

    /**
     * 要被增强的方法集合
     */
    protected List<Method> methodNames;

    /**
     * 被增强的方法，是否要被拦截，true表示要被拦击，此时方法不会被触发，false表示不要被拦截，方法可以正常被触发
     */
    protected boolean isIntercept;

    @Override
    public void init(Class<?> aClass) {
        enhancer = new Enhancer();
        enhancer.setSuperclass(aClass);
        initMethod(aClass);
        proxy();
    }

    /**
     * 设置代理类对象
     *
     * @param proxy 代理类对象
     */
    public void setProxy(Object proxy) {
        this.proxy = proxy;
        Class<?> aClass = proxy.getClass();
        AgentClass agentClass = aClass.getDeclaredAnnotation(AgentClass.class);
        init(agentClass.supperClass()[0]);
    }

    /**
     * 重写吃方法，在目标方法触发前，触发
     *
     * @param objects 目标方法参数
     * @throws InvocationTargetException 方法调用失败时抛出此异常
     * @throws IllegalAccessException    方法参数错误时，抛出此异常
     */
    public abstract void before(Object... objects) throws InvocationTargetException, IllegalAccessException;

    /**
     * 重写此方法，在目标方法触发之后，触发
     *
     * @param objects 目标方法参数
     * @throws InvocationTargetException 方法调用失败时抛出此异常
     * @throws IllegalAccessException    方法参数错误时，抛出此异常
     */
    public abstract void after(Object... objects) throws InvocationTargetException, IllegalAccessException;

    /**
     * 设置被代理的对象，在增强后，的增强逻辑
     */
    private void proxy() {
        enhancer.setCallback((MethodInterceptor) (o, method, objects, methodProxy) -> {
            if (!checkMethod(method)) {
                return methodProxy.invoke(o, objects);
            }
            Object result = null;
            // 注入前置逻辑
            before(objects);
            if (!isIntercept) {
                result = methodProxy.invokeSuper(o, objects);
            }
            // 注入后置逻辑
            after(objects);
            return result;
        });
    }

    /**
     * 验证传入的方法，是否存在于增强方法的列表中
     *
     * @param method 带验证的方法参数
     * @return true 表示被验证的方法，存在于增强方法列表中 false 表示此方法不在增强方法列表中
     */
    private boolean checkMethod(Method method) {
        for (Method methodName : methodNames) {
            String name = method.getName();
            String cacheName = methodName.getName();
            if (name.equals(cacheName)) {
                Class<?>[] types = methodName.getParameterTypes();
                Class<?>[] parameterTypes = method.getParameterTypes();
                boolean equals = Arrays.equals(types, parameterTypes);
                if (equals) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * 初始化被增强的对象，将所有的方法对象，保存至指定列表中
     *
     * @param aClass 被增强的对象的类对象
     */
    protected void initMethod(Class<?> aClass) {
        Method[] methods = aClass.getDeclaredMethods();
        for (Method method : methods) {
            AgentMethod agentMethod = method.getDeclaredAnnotation(AgentMethod.class);
            if (agentMethod == null) {
                continue;
            }
            if (methodNames == null) {
                methodNames = new ArrayList<>();
            }
            methodNames.add(method);
        }
    }

    @Override
    public <T> T createObject() {
        return (T) enhancer.create();
    }
}
